
#include "ServiceBase.h"
#include "Common/Errors.h"
#include "Common/Strings.h"
#include "Runtime.Low/Utils.h"

using namespace std;
using namespace ttg;
using namespace ttg::structures;
using namespace ttg::services;

//-------------------
//--- ServiceBase ---
//-------------------

ServiceBase *ServiceBase::ref = NULL;

Service *ServiceBase::findAndStart(const char *name, const char *params)
{
	Service *res = objects->get(name);
	if (res == NULL || res->isStarted())
		return res;
	vector<Service *> deps = vector<Service *>(res->getDependencies().size());
	for (size_t i = 0; i < res->getDependencies().size(); i++)
	{
		deps[i] = findAndStart(res->getDependencies()[i].c_str());
		if (deps[i] == NULL)
			throw Error(Error::FATAL, "ServiceBase", "findAndStart", "Cannot find object", name);
	}
	res->start(deps, params);
	return res;
}

void ServiceBase::findAndStop(const char *name)
{
	Service *res = objects->get(name);
	if (res == NULL || !res->isStarted())
		return;
	for (size_t i = 0; i < res->getDependents().size(); i++)
	{
		findAndStop(res->getDependents()[i].c_str());
	}
	res->stop();
}

ServiceBase::ServiceBase(size_t expected_size)
{
	this->objects = Dictionary<Service>::create(expected_size);
}

void ServiceBase::add(Service *object)
{
	if (object != NULL && objects->get(object->getName()) == NULL)
	{
		//Adding object.
		objects->add(object);
		if (object->isUpdatable())
			updatables.push_back(object);
	}
	else
		SAFE_DELETE(object);
}

void ServiceBase::checkRefs()
{
	HRESULT hres;
	if (FAILED(hres = CoInitialize(NULL)) &&
		(hres != RPC_E_CHANGED_MODE && hres != S_FALSE))
		throw Error(Error::FATAL, "BaseServices", "create", "Cannot init COM", hres);
	if (Logger::getRef() == NULL ||
		SettingsKeeper::getRef() == NULL ||
		PublisherBase::getRef() == NULL ||
		Timer::getRef() == NULL ||
		DllLoader::getRef() == NULL ||
		CPUAnalyzer::getRef() == NULL ||
		DeviceAnalyzer::getRef() == NULL ||
		TaskScheduler::getRef() == NULL ||
		ttg::utils::UtilManager::getRef() == NULL)
		throw Error(Error::FATAL, "ServiceBase", "checksRefs",
					"Internal: one or more singletons was not created");
}

void ServiceBase::update(float step)
{
	for (size_t i = 0; i < updatables.size(); i++)
		updatables[i]->update(step);
}

void ServiceBase::start()
{
	checkRefs();
	list<Service *> en = objects->toList();
	list<Service *>::iterator it = en.begin();
	while (it != en.end())
	{
		if (!(*it)->isStarted() &&
			findAndStart((*it)->getName()) == NULL)
			throw Error(Error::FATAL, "ServiceBase", "start", "Cannot find object", (*it)->getName());
		it++;
	}
	threads::CurrentThread::sleep(0.25);
	on_start.notify(true);
}

void ServiceBase::start(const char *services)
{
	checkRefs();
	if (services != NULL)
	{
		strings::StringSplitter sp;
		sp.setSplitter('&', false);
		sp.setSplitter('\n', false);
		sp.setString(services);
		for (size_t i = 0; i < sp.getCount(); i++)
		{
			strings::StringSplitter params;
			params.setSplitter('(', false);
			params.setSplitter(')', false);
			params.setString(sp[i]);
			if (params.getCount() == 0 ||
				findAndStart(params[0], params.getCount() >= 2 ? params[1] : NULL) == NULL)
				throw Error(Error::FATAL, "ServiceBase", "start", "Cannot find one object", services);
		}
	}
	threads::CurrentThread::sleep(0.25);
	on_start.notify(true);
}

void ServiceBase::stop()
{
	on_stop.notify(true);
	//Clearing references.
	list<Service *> en = objects->toList();
	list<Service *>::iterator it = en.begin();
	while (it != en.end())
	{
		(*it)->getDependents().clear();
		it++;
	}
	//Building new references.
	it = en.begin();
	while (it != en.end())
	{
		std::vector<string> &deps = (*it)->getDependencies();
		for (size_t i = 0; i < deps.size(); i++)
		{
			Service *object = get(deps[i].c_str());
			if (object != NULL)
				object->getDependents().push_back((*it)->getName());
		}
		it++;
	}
	//Stopping.
	it = en.begin();
	while (it != en.end())
	{
		findAndStop((*it)->getName());
		it++;
	}
	threads::CurrentThread::sleep(0.25);
}

void ServiceBase::stop(const char *services)
{
	on_stop.notify(true);
	//Clearing references.
	list<Service *> en = objects->toList();
	list<Service *>::iterator it = en.begin();
	while (it != en.end())
	{
		(*it)->getDependents().clear();
		it++;
	}
	//Building new references.
	it = en.begin();
	while (it != en.end())
	{
		std::vector<string> &deps = (*it)->getDependencies();
		for (size_t i = 0; i < deps.size(); i++)
		{
			Service *object = get(deps[i].c_str());
			if (object != NULL)
				object->getDependents().push_back((*it)->getName());
		}
		it++;
	}
	//Stopping.
	strings::StringSplitter sp;
	sp.setSplitter('&', false);
	sp.setSplitter('\n', false);
	sp.setString(services);
	for (size_t i = 0; i < sp.getCount(); i++)
		findAndStop(sp[i]);
	threads::CurrentThread::sleep(0.25);
}

ServiceBase *ServiceBase::getRef()
{
	if (ref == NULL)
		ref = new ServiceBase();
	return ref;
}

ServiceBase::~ServiceBase()
{
	SAFE_DELETE(objects);
}
